import axios, {AxiosInstance, AxiosError, AxiosRequestConfig, AxiosResponse, AxiosHeaders} from 'axios'
import { useMessage } from 'naive-ui'
// 导入naive - ui库中的useMessage函数，用于显示消息提示
const message = useMessage()

// 数据返回的接口
// 定义请求响应参数，不含data
// 这个接口描述了一个基本的请求响应结构，包含状态码和消息
interface Result {
  code: number
  msg: string
}

// 请求响应参数，包含data
// 这个接口继承自Result接口，并增加了一个可选的data属性，用于存放响应的数据
// T是一个泛型参数，默认类型为any，表示可以是任何类型的数据
interface ResultData<T = any> extends Result {
  data?: T
}


const URL: string = ''


// 定义一个枚举类型，用于表示请求相关的状态码或常量
enum RequestEnums {
  TIMEOUT = 20000,
  // 设置请求超时时间为20000毫秒（20秒）
  OVERDUE = 600, // 登录失效
  FAIL = 999, // 请求失败
  SUCCESS = 200, // 请求成功
}


const config = {
  baseURL: URL as string, // 默认地址
  // 将之前定义的URL字符串作为axios的基础URL，如果为空字符串则可能需要在别处动态设置
  timeout: RequestEnums.TIMEOUT as number,// 设置超时时间
  // 使用RequestEnums中的TIMEOUT值设置axios的请求超时时间
  withCredentials: true, // 跨域时候允许携带凭证
  // 当进行跨域请求时，允许携带凭证（如cookies等）
}


class RequestHttp {
  service: AxiosInstance // 定义成员变量并指定类型
  // 定义一个AxiosInstance类型的成员变量service，用于存储axios实例

  public constructor(config: AxiosRequestConfig) {
    // 实例化axios
    this.service = axios.create(config as AxiosRequestConfig)
    // 使用传入的配置对象config创建一个axios实例，并赋值给service

    /**
     * 请求拦截器
     * 客户端发送请求 -> [请求拦截器] -> 服务器
     * token校验(JWT) : 接受服务器返回的token,存储到vuex/pinia/本地储存当中
     */
    this.service.interceptors.request.use(
      (config: AxiosRequestConfig) => {
        const token = localStorage.getItem('token') || ''
         const headers = new AxiosHeaders()
         headers.set('x-access-token', token)
        // 从本地存储中获取token，如果不存在则设置为空字符串
        return {
         ...config,
          headers
        }
      },
      (error: AxiosError) => {
        // 请求报错
        Promise.reject(error)
        // 如果请求拦截器中发生错误，直接拒绝这个错误，将错误传递给调用者
      }
    )

    /**
     * 响应拦截器
     * 服务器换返回信息 -> [拦截统一处理] -> 客户端JS获取到信息
     */
    this.service.interceptors.response.use(
      (response: AxiosResponse) => {
        const {data, config} = response // 解构
        // 从响应对象中解构出data和config属性
        if (data.code === RequestEnums.OVERDUE) {
          // 登录信息失效，应跳转到登录页面，并清空本地的token
          localStorage.setItem('token', '')
          // router.replace({
          //   path: '/login'
          // })
          return Promise.reject(data) // 如果响应的状态码表示登录失效，则清除本地存储中的token，并拒绝这个响应数据
          
        }
        // 全局错误信息拦截（防止下载文件得时候返回数据流，没有code，直接报错）
        if (data.code && data.code!== RequestEnums.SUCCESS) {
          message.error(data) // 此处也可以使用组件提示报错信息
          return Promise.reject(data)
          // 如果响应数据包含状态码且不是表示成功的状态码，则显示错误消息并拒绝这个响应数据
        }
        return data
      },
      (error: AxiosError) => {
        const {response} = error
        // 从错误对象中解构出响应对象response
        if (response) {
          this.handleCode(response.status)
          // 如果存在响应对象，则根据响应的状态码调用handleCode方法进行处理
        }
        if (!window.navigator.onLine) {
          message.error('网络连接失败')
          // 如果网络离线，则显示网络连接失败的错误消息
          // 可以跳转到错误页面，也可以不做操作
          // return router.replace({
          //   path: '/404'
          // })
        }
      }
    )
  }
  handleCode(code: number):void {
    switch(code) {
      case 401:
        message.error('登录失败，请重新登录')
        break
      default:
        message.error('请求失败')
        break
    }
  }

  get<T>(url: string, params?: object): Promise<ResultData<T>> {
    return this.service.get(url, {params})
  }
  post<T>(url: string, params?: object): Promise<ResultData<T>> {
    return this.service.post(url, params)
  }
  put<T>(url: string, params?: object): Promise<ResultData<T>> {
    return this.service.put(url, params)
  }
  delete<T>(url: string, params?: object): Promise<ResultData<T>> {
    return this.service.delete(url, {params})
  }
}


export default new RequestHttp(config) // 导出一个使用之前定义的 config 配置创建的RequestHttp类的实例对象